<template>
  <el-select
    v-model="selectValue"
    ref="searchSelect"
    :placeholder="placeholder"
    :multiple="multiple"
    :size="size"
    :filterable="filterable"
    :filter-method="handleFilterMethod"
    :disabled="disabled"
    collapse-tags
    clearable
    @change="onSelectChange"
    @clear="onSelectClear"
    :loading="loading">

    <el-option :value="optionValue" class="dropdown-option">
      <el-tree v-if="treeData.length"
               class="dropdown-option__tree"
               :data="treeData"
               :show-checkbox="multiple"
               ref="treeRef"
               style="font-weight: 500;"
               :props="defaultProps"
               highlight-current
               :current-node-key="currentNodeKey"
               :default-expand-all="defaultExpandAll"
               :default-checked-keys="defaultCheckedKeys"
               :filter-node-method="filterNode"
               :node-key="nodeKey"
               @check-change="handleCheckChange"
               @node-click="clickNode"
      ></el-tree>
      <div v-if="!treeData.length" style="width: 100%;height: 100%;background-color: #FFFFFF;text-align: center;">
        暂无数据
      </div>
      <div id="load" v-show="load" style="position: absolute;left: 0;top: 0;height: 200px;width: 100%;"></div>
    </el-option>
  </el-select>
</template>

<!-- /**
 * 组件说明
 * 属性:
 * 参数                     说明                       类型                    默认值
 * ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 * placeholder              输入框占位文本                String                '请选择'
 * defaultProps             需要使用的展示字段值          Object                {children: 'children',label: 'label'}
 * data                     tree 的数据源                 Array                 []
 * filterable               是否开启搜索功能              Boolean                false
 * multiple                   tree下拉是否多选              Boolean                false
 * defaultExpandAll         tree是否展开全部节点          Boolean                 false
 * disabled                   是否禁止操作                Boolean                Array
 * size                    el-option大小尺寸选择          String                 medium
 *
 * 事件:
 *  change  获取选中的值
 *  select  获取选中对象或数组
 */ -->
<script>
const deepFind = (arr, condition, children) => {
  let main = [];
  try {
    (function poll(arr, level) {
      if (!Array.isArray(arr)) return;
      for (let i = 0; i < arr.length; i++) {
        const item = arr[i];
        main[level] = item;
        const isFind = (condition && condition(item, i, level)) || false;
        if (isFind) {
          throw Error;
        } else if (children && item[children] && item[children].length) {
          poll(item[children], level + 1);
        } else if (i === arr.length - 1) {
          main.length = main.length - 1;
        }
      }
    })(arr, 0);
  } catch (err) {}
  return main;
};

export default {
  name: 'base-tree-select',
  model: {
    prop: 'value',
    event: 'change',
  },
  props: {
    value: null,
    filterable: {
      type: Boolean, // 是否开启搜索
      default: false,
    },
    multiple: {
      type: Boolean, // 是否多选
      default: false,
    },
    placeholder: {
      type: String,
      required: false,
      default: '请选择',
    },
    size: {
      type: String,
      default: 'medium',
    },
    disabled: {
      type: Boolean,
      default: false,
    },
    data: {
      type: Array, // 数据
      default: () => ([]),
    },
    defaultProps: { // 需要使用的展示字段值
      type: Object,
      default: () => ({
        children: 'children',
        label: 'label',
      }),
    },
    defaultExpandAll: {
      type: Boolean, // 是否展开节点
      default: false,
    },
    nodeKey: {
      type: String,
      default: 'id',
    },
    // 是否允许选择父节点
    enableCheckParentNode: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      load: false,
      selectValue: '',
      optionValue: [],
      defaultCheckedKeys: [], // 多选时，默认选中的值
      currentNodeKey: '', // 单选时，高亮默认选中的值
      loading: false,
      deferTimer: null, // 多选复选框高频查找的数据使用到的延时器变量
      loadingTips: null,
      callbackDefault: null,
      treeData: [], // 渲染树的变量
      firstTime: false, // 初次加载 状态
    };
  },
  watch: {
    // 更新数据/清空输入框
    data(n) {
      if (this.firstTime) {
        if (Array.isArray(n)) {
          this.selectValue = '';
          this.optionValue = [];
          this.treeData = n;
          this.defaultCheckEvent(true);
        } else {
          console.error('data 属性必须是一个Array');
        }
      }
    },
  },
  created() {
    let dataLoading = true;

    // 等待 接口树 数据获取完成
    const dataTerr = () => {
      if (!this.treeData.length && dataLoading) {
        this.selectValue = '获取数据中...';
        setTimeout(() => {
          this.treeData = this.data;
          dataTerr();
        }, 400);
      } else if (this.treeData.length) {
        dataLoading = false;
        this.selectValue = '';
        // 是否开启默认勾选
        if (this.value) {
          this.defaultCheckEvent();
        }
        // 初次加载 完成
        this.firstTime = true;
      }
    };

    dataTerr();
    // 2.5s后不管有没要获取到数据 都停止
    setTimeout(() => {
      dataLoading = false;
      this.firstTime = true;
    }, 2500);
  },

  methods: {
    // 过滤数据 返回搜索结果
    filterNode(value, data) {
      return value ? data[this.defaultProps.label].indexOf(value) !== -1 : true;
    },

    /**
     * 下拉搜索方法回调函数
     * @param val
     */
    handleFilterMethod(val) {
      this.$refs.treeRef.filter(val);
    },

    // select框值改变时候触发的事件
    onSelectChange(value) {
      if (!this.multiple || !this.treeData.length) return;
      const overOption = this.optionValue.filter(item => value.some(o => o === item[this.defaultProps.label]));
      this.$refs.treeRef.setCheckedNodes(overOption); // 设置勾选的值
    },

    /**
     * 清空选中的值
     */
    onSelectClear() {
      if (!this.$refs.treeRef) return;

      if (this.multiple) {
        this.$refs.treeRef.setCheckedNodes([]);
        this.$refs.treeRef.setCurrentKey(null);
        this.selectValue = [];
        this.optionValue = [];
      } else {
        this.selectValue = '';
        this.optionValue = [];
        this.$refs.treeRef.setCurrentKey(null);
      }
      this.$emit('change', this.multiple ? [] : '');
      this.$emit('select', this.multiple ? [] : '');
    },

    // 默认勾选
    defaultCheckEvent(lock) {
      // 避免 多个监听数据更新时同时并发多次
      if (lock) {
        this.firstTime = false;
      }
      // 筛选出数据存放
      let defaultData = [];
      // 根据树id 递归树 筛选出对应的数据
      if (this.multiple && Array.isArray(this.value)) { // 多选
        this.value.forEach((id, k) => {
          let deepFindItemPath = deepFind(this.treeData, (item, index, level) => item[this.nodeKey] === id, this.defaultProps.children);
          deepFindItemPath.forEach((v, l) => {
            if (v[this.nodeKey] === id) {
              defaultData.push(v);
            }
          });
        });
      } else { // 单选
        let deepFindItemPath = deepFind(this.treeData, (item, index, level) => item[this.nodeKey] === this.value, this.defaultProps.children);
        deepFindItemPath.forEach((v, l) => {
          if (v[this.nodeKey] === this.value) {
            defaultData.push(v);
          }
        });
      }

      // 更新输入框内的默认勾选值
      let arrLabel = defaultData.map(item => item[this.defaultProps.label]);
      this.optionValue = defaultData;
      this.selectValue = this.multiple ? arrLabel : arrLabel[0];
      if (this.multiple) {
        this.defaultCheckedKeys = this.optionValue.map(o => o[this.nodeKey]);
      } else {
        this.currentNodeKey = this.optionValue.length > 0 ? this.optionValue.map(o => o[this.nodeKey])[0] : '';
      }

      // 解除 状态
      if (lock) {
        this.firstTime = true;
      }
      if (!this.selectValue.length) {
        this.onSelectClear();
      }
    },

    // 单选模式事件
    clickNode(data, node, obj) {
      if (this.multiple) { // 多选
        if (node.isLeaf) {
          this.$refs.treeRef.setChecked(data, !node.checked, true);
        }
        return;
      }

      // 单选
      if (!this.enableCheckParentNode && !node.isLeaf) {
        return;
      }

      this.optionValue = [data];
      this.selectValue = data[this.defaultProps.label];
      this.$refs.searchSelect.blur(); // 失去焦点 关闭下拉框
      // 传递数据给父
      this.$emit('change', data[this.nodeKey]);
      this.$emit('select', data);
    },

    // 获取当前复选框 选中的值 赋值到输入框里
    handleCheckChange() {
      if (this.deferTimer == null) {
        this.load = true;
        this.loadingTips = this.$loading({
          lock: true,
          text: 'Loading',
          spinner: 'el-icon-loading',
          background: 'rgba(255, 255, 255, 0.5)',
          target: document.getElementById('load'),
        });
      }
      // 这里两个true，1. 是否只是叶子节点 2. 是否包含半选节点（就是使得选择的时候不包含父节点）
      let res = this.$refs.treeRef.getCheckedNodes(true, true);
      let arrLabel = res.map(o => o[this.defaultProps.label]);

      clearTimeout(this.deferTimer);
      this.deferTimer = setTimeout(() => {
        this.optionValue = res;
        this.selectValue = arrLabel;
        this.load = false;
        this.loadingTips.close();
        this.deferTimer = null;
        this.$emit('change', res.map(o => o[this.nodeKey]));
        this.$emit('select', res);
      }, 200);
    },
  },
};
</script>

<style lang="scss" scoped>
.el-select-dropdown {
  .el-select-dropdown__list {
    .el-select-dropdown__item {
      padding: 0;

      &.hover, &:hover {
        background-color: #fff;
      }

      ::v-deep .el-scrollbar__wrap {
        overflow-x: auto;
      }
    }
  }
}

.dropdown-option {
  background-color: #fff;
  position: relative;
  width: 100%;
  height: auto;
}

.dropdown-option__tree {
  font-weight: 400;
}


.el-tree-node__content {
  width: max-content;
  min-width: 100%;
  color: #fff;
}

.el-tree-node,
.el-tree-node__children {
  min-width: 100%;
  width: max-content;
}

.el-tree-node__label {
  width: auto;
  overflow: hidden;
  text-overflow: ellipsis !important;
  white-space: nowrap !important;
}

.el-tree-node__children .el-tree-node__label {
  width: auto;
  overflow: hidden;
  text-overflow: ellipsis !important;
  white-space: nowrap !important;
}
</style>
